/*******************************************************************************
 * @file Copyright 2009 The CREATE Lab at CMU
 * 
 *       This file is part of JubJub, an open source project.
 *       http://code.google.com/p/jubjub/
 * 
 *       Redistribution and use in source and binary forms, with or without
 *       modification, are permitted provided that the following conditions are
 *       met:
 * 
 *       Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 * 
 *       Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 * 
 *       Neither the name of Carnegie Mellon nor the names of its contributors
 *       may be used to endorse or promote products derived from this software
 *       without specific prior written permission.
 * 
 *       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *       "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *       LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 *       PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *       HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *       SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *       LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *       THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *       (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *       OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/

package edu.cmu.ri.createlab.jj.visitors;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;

import edu.cmu.ri.createlab.jj.blocks.*;
import edu.cmu.ri.createlab.jj.blocks.BlockDefs.AccessTypes;
import edu.cmu.ri.createlab.jj.blocks.BlockDefs.BlockTypes;
import edu.cmu.ri.createlab.jj.blocks.BlockDefs.CallTypes;
import edu.cmu.ri.createlab.jj.blocks.BlockDefs.ExpressTypes;
import edu.cmu.ri.createlab.jj.blocks.BlockDefs.VarTypes;
import edu.cmu.ri.createlab.jj.blocks.TaggedCodePiece.TCPType;
import edu.cmu.ri.createlab.jj.projects.JJClass;
import edu.cmu.ri.createlab.jj.projects.JJLibrary;
import edu.cmu.ri.createlab.jj.projects.JJProject;

/**
 * 
 * @author Erik Pasternak
 *
 * Writes a class structure to a buffer and then outputs it to either a file
 * or BlueJ.
 * 
 * Java writer will traverse a JJClass and all its elements to generate Java
 * code for the class which can then be output to a file or BlueJ editor. The
 * code is not output until final is called and the write flags for the desired
 * outputs must be set using setWrite.
 */
public class BVJavaWriter implements BlockVisitorInterface
{
   ///The generated code
   private StringBuffer code;
   ///The name of the class being traversed
   private String className;
   ///The filename that will be generated from the className
   private String filename;
   ///How far the current line of code should be indented
   private int tabCount;
   ///Whether this should output to a file
   private boolean writeToFile;
   ///Whether this should output to a BlueJ editor
   private boolean writeToBlueJ;
   
   ///Creates a new writer. Sets an initial size for the code buffer
   ///and initializes all other params.
   public BVJavaWriter()
   {
      code = new StringBuffer(20000);
      filename = null;
      className = null;
      tabCount = 0;
      writeToFile = false;
      writeToBlueJ = false;
   }
   
   ///Sets the flags for writing to the different outputs
   public void setWrite(boolean toFile, boolean toBlueJ)
   {
      writeToFile = toFile;
      writeToBlueJ = toBlueJ;
   }
   
   public String getCode()
   {
      return code.toString();
   }
   
   ///clears any code and the file/class names.
   ///also sets the write flags to false.
   public void clear()
   {
      code.delete(0, code.length());
      filename = null;
      className = null;
      writeToFile = false;
      writeToBlueJ = false;
   }
   
   ///inserts the given number of tabs into the code,
   ///where three spaces are used as a tab.
   private void addTabs(int numTabs)
   {
      for(int i = 0; i < numTabs; i++)
         code.append("   ");
   }
   
   private void writeComment(String c)
   {
      if(c != null && !(c.length() == 0))
      {
         code.append("\n");
         addTabs(tabCount);
         code.append("/**\n");
         String tempStr = c;
         tempStr = tempStr.replace("\\*/", "x/");
         addTabs(tabCount);
         code.append(tempStr);
         code.append("\n");
         addTabs(tabCount);
         code.append("*/");
      }
   }
   

   ///Visits a BlockBlock and generates Java output from its contents
   
   public BVReturn visit(BlockBlock block)
   {
      ///Check to see if this is just a reference to external code first
      ///We don't write a method body into the file for external code, just 
      ///reference it.
      if(block == null) return null;
      ///First, write the comment
      code.append("\n");
      writeComment(block.getComment());

      code.append("\n");
      ///Then, create the method declaration
      ///By first setting the visibility
      addTabs(tabCount);
      if(block.isPublic()) code.append("public ");
      else if(block.isProtected()) code.append("protected ");
      else if(block.isPrivate()) code.append("private ");
      ///Then any other keywords
      if(block.isStatic()) code.append("static ");
      if(block.isFinal()) code.append("final ");
      ///Add the return type
      ///If no returns it's void
      if(block.getReturns().size() == 0) code.append("void");
      ///Make sure the return references a valid block
      else if(block.getReturns().get(0).getBlock() == null)
         code.append("void");
      ///If the return is an object it's the name of the object's class
      else if (((VarBlock)block.getReturns().get(0).getBlock()).getVarType() == VarTypes.OBJECT)
         code.append(((VarBlock)block.getReturns().get(0).getBlock()).getObjectType());
      ///Otherwise it's just the string representation of the basic type
      else code.append(((VarBlock)block.getReturns().get(0).getBlock()).getVarType().toString());
      ///Add the callable name of the method next
      code.append(" ");
      code.append(block.getCallName() + " ");
      ///Open the parameters braces
      code.append("(");
      ///add parameters
      Iterator<VarBlock> vIt = block.getParams().iterator();
      while(vIt.hasNext())
      {
         VarBlock tempVar = vIt.next();
         if(tempVar.getVarType() == VarTypes.OBJECT)
         {
            code.append(tempVar.getObject().getObjectType());
         }
         else
         {
            code.append(tempVar.getVarType().toString());
         }
         code.append(" " + tempVar.getCallName());
         if(vIt.hasNext()) code.append(", ");
      }
      ///Close the parameters and open the declaration
      code.append(")\n");
      addTabs(tabCount);
      code.append("{");
      tabCount++;
      ///add the variables in this block
      vIt = block.getVars().iterator();
      while(vIt.hasNext())
      {
         vIt.next().accept(this);
      }
      ///add in all the code
      Iterator<RefBlock> rIt = block.getCode().iterator();
      while(rIt.hasNext())
      {
         rIt.next().accept(this);
      }
      ///Add the return
      code.append("\n");
      addTabs(tabCount);
      code.append("return");
      if(block.getReturns().size() != 0 && block.getReturns().get(0).getBlock() != null)
      {
         code.append(" ");
         if(((VarBlock)block.getReturns().get(0).getBlock()).isLiteral())
            code.append(((VarBlock)block.getReturns().get(0).getBlock()).getValue(0));

         else
            code.append(((VarBlock)block.getReturns().get(0).getBlock()).getCallName());
      }
      code.append(";");
      ///Close the declaration
      tabCount--;
      code.append("\n");
      addTabs(tabCount);
      code.append("}");
      ///all finished
      return null;
   }

   ///Visits a CodeBlock and generates Java from its contents
   
   public BVReturn visit(CodeBlock block)
   {
      ///Check to see if this is just a reference to external code first
      ///We don't write a method body into the file for external code, just 
      ///reference it.
      if(block == null || block.getTaggedCode() == null) return null;
      ///First, write the comment
      code.append("\n");
      writeComment(block.getComment());
      
      ///Then, create the method declaration
      ///By first setting the visibility
      code.append("\n");
      addTabs(tabCount);
      if(block.isPublic()) code.append("public ");
      else if(block.isProtected()) code.append("protected ");
      else if(block.isPrivate()) code.append("private ");
      ///Then any other keywords
      if(block.isStatic()) code.append("static ");
      if(block.isFinal()) code.append("final ");
      ///Add the return type
      ///If no returns it's void
      if(block.getReturns().size() == 0) code.append("void");
      ///If the return is an object it's the name of the object's class
      else if (block.getReturns().get(0).getVarType() == VarTypes.OBJECT)
         code.append(block.getReturns().get(0).getObject().getObjectType());
      ///Otherwise it's just the string representation of the basic type
      else code.append(block.getReturns().get(0).getVarType().toString());
      code.append(" ");
      ///Add the callable name of the method next
      code.append(block.getCallName());
      ///Open the parameters braces
      code.append("(");
      ///add parameters
      Iterator<VarBlock> vIt = block.getParams().iterator();
      while(vIt.hasNext())
      {
         VarBlock tempVar = vIt.next();
         if(tempVar.getVarType() == VarTypes.OBJECT)
         {
            code.append(tempVar.getObjectType());
         }
         else
         {
            code.append(tempVar.getVarType().toString());
         }
         code.append(" " + tempVar.getCallName());
         if(vIt.hasNext()) code.append(", ");
      }
      ///Close the parameters and open the declaration
      code.append(")\n");
      addTabs(tabCount);
      code.append("{\n");
      tabCount++;
      addTabs(tabCount);
      ///Throw in all the code
      Iterator<TaggedCodePiece> tIt = block.getTaggedCode().iterator();
      while(tIt.hasNext())
      {
         //TODO maybe add some smart tabbing, would take some doing that won't
         //happen right now.
         code.append(tIt.next().getContent());
      }
      tabCount--;
      ///Close the declaration
      code.append("\n");
      addTabs(tabCount);
      code.append("}");
      ///all finished
      return null;
   }

   ///ExpressionBlock types should not be visited, only references to them
   
   public BVReturn visit(ExpressionBlock block)
   {
      return null;
   }

   ///FlowBlock types should not be visited, only references to them
   
   public BVReturn visit(FlowBlock block)
   {
      return null;
   }

   ///Visits a VarBlock and generates the initializer for it
   
   public BVReturn visit(VarBlock block)
   {
      ///When visiting a VarBlock we just need to generate
      ///the comment and a declaration
      //addTabs(tabCount);
      writeComment(block.getComment());
      
      ///Add any access keywords
      code.append("\n");
      addTabs(tabCount);
      if(block.isPublic()) code.append("public ");
      else if(block.isProtected()) code.append("protected ");
      else if(block.isPrivate()) code.append("private ");
      if(block.isStatic()) code.append("static ");
      if(block.isFinal()) code.append("final ");
      
      ///Add the type
      if(block.getVarType() == VarTypes.OBJECT)
         code.append(block.getObject().getObjectType());
      else code.append(block.getVarType().toString());
      code.append(" ");
      
      ///Finally, the name
      code.append(block.getCallName());
      ///and whatever value it's initialized to if it's a basic type
      if(block.getVarType() != VarTypes.OBJECT)
      {
         code.append(" = ");
         if(block.getValueLength() > 1) code.append("{");
         for(int i = 0; i < block.getValueLength(); i++)
         {
            code.append(block.getValue(i));
            if(i+1 < block.getValueLength()) code.append(", ");
         }
         if(block.getValueLength() > 1) code.append("}");
      }
      code.append(";");
      
      return null;
   }

   ///Visits a RefBlock and calls a helper function depending on the type of
   ///block it references
   
   public BVReturn visit(RefBlock block)
   {
      ///whatever type of ref this is add the comment if it has one
      writeComment(block.getComment());
      //Check what type of block it is and take the appropriate action
      switch(block.getBlock().getType())
      {
      case BLOCK:
         blockRef(block, true);
         break;
      case CODE:
         codeRef(block, true);
         break;
      case FLOW:
         flowRef(block);
         break;
      case EXPRESSION:
         expressionRef(block, true);
         break;
      case VAR:
         varRef(block);
         break;
      case OBJECT:
         objectRef(block);
         break;
      case COMMENT:
         commentRef(block);
         break;
      }      
      return null;
   }
   
   ///Does checks to see what kind of parameter this is and writes it
   private void writeParam(RefBlock ref)
   {
      switch(ref.getBlock().getType())
      {
      case VAR:
      {
         VarBlock tempVar = (VarBlock)ref.getBlock();
         if(tempVar.isLiteral()) code.append(tempVar.getValue(0));
         else code.append(tempVar.getCallName());
         break;
      }
      default:
         code.append(Block.toCallName(ref.getBlock().getName()));
         break;
      }
      
   }
   
   /**
    * @param block The reference being visited.
    * @param top Whether this is a top level reference call or part of another RefBlock.
    * @return
    * 
    * Outputs code for a RefBlock that references a BlockBlock.
    */
   private BVReturn blockRef(RefBlock block, boolean top)
   {
      
      BlockBlock tempBlock = (BlockBlock)block.getBlock();
      if(tempBlock.getCallType() == CallTypes.METHOD)
      {
         if(top) 
         {
            code.append("\n");
            addTabs(tabCount);
         }
         ///If we have a return put that in first
         if(block.getReturns().size() > 0 && top)
         {
            code.append(Block.toCallName(block.getReturns().get(0).getName()));
            code.append(" = ");
         }
         ///Now write the method call
         if(tempBlock.getPrefix() != null)
            code.append(tempBlock.getPrefix());
         code.append(tempBlock.getCallName());
         ///add the parameters
         code.append(" (");
         Iterator<RefBlock> it = block.getParams().iterator();
         while(it.hasNext())
         {
            RefBlock tempRef = it.next();
            if(tempRef.getBlock() != null && tempRef.getBlock().getType() == BlockTypes.VAR)
               writeParam(tempRef);
            else if(tempRef.getBlock() != null && tempRef.getBlock().getType() == BlockTypes.EXPRESSION)
               expressionRef(tempRef, false);
            if(it.hasNext())
               code.append(", ");
         }
         code.append(")");
         if(top) code.append(";");
      }else
      {
         //TODO add code for writing inline blocks
      }
      return null;
   }
   

   /**
    * @param block The reference being visited.
    * @param top Whether this is a top level reference call or part of another RefBlock.
    * @return
    * 
    * Outputs code for a RefBlock that references a CodeBlock
    */
   private BVReturn codeRef(RefBlock block, boolean top)
   {
      CodeBlock tempBlock = (CodeBlock)block.getBlock();
      if(tempBlock.getCallType() == CallTypes.METHOD)
      {
         if(top) 
         {
            code.append("\n");
            addTabs(tabCount);
         }
         ///If we have a return put that in first
         if(block.getReturns().size() > 0 && top)
         {
            code.append(Block.toCallName(block.getReturns().get(0).getName()));
            code.append(" = ");
         }
         ///Now write the method call
         if(tempBlock.getPrefix() != null)
            code.append(tempBlock.getPrefix());
         code.append(tempBlock.getCallName());
         ///add the parameters
         code.append(" (");
         Iterator<RefBlock> it = block.getParams().iterator();
         while(it.hasNext())
         {
            RefBlock tempRef = it.next();
            if(tempRef.getBlock() != null && tempRef.getBlock().getType() == BlockTypes.VAR)
               writeParam(tempRef);
            else if(tempRef.getBlock() != null && tempRef.getBlock().getType() == BlockTypes.EXPRESSION)
               expressionRef(tempRef, false);
            if(it.hasNext())
               code.append(", ");
         }
         code.append(")");
         if(top) code.append(";");
      }else
      {
         //TODO add code for writing inline blocks
      }
      return null;
   }

   ///Outputs FlowBlock code for this RefBlock
   private BVReturn flowRef(RefBlock block)
   {
      FlowBlock tempBlock = (FlowBlock)block.getBlock();
      int index = block.getIndex();

      code.append("\n");
      ///If it's a middle or end section close the previous section
      if(index > 0)
      {
         tabCount--;
         addTabs(tabCount);
         code.append("}");
      }
      else addTabs(tabCount);
      ///set the swaps for this section
      tempBlock.setSwapNames(block.getSwapNames(), index);
      Iterator<TaggedCodePiece> tIt = tempBlock.getCode(index).iterator();
      while(tIt.hasNext())
      {
         //TODO add code to check swapnames to make sure it's not empty if
         //we're accessing code
         TaggedCodePiece tcp = tIt.next();
         if(tcp.getType() == TCPType.CODE)
         {
            code.append(tcp.getContent());
         }
         else
         {
            code.append(tempBlock.getSwapNames(index)[tcp.getIndex()]);
         }
      }
      if(index < 3)
      {
         code.append("\n");
         addTabs(tabCount);
         code.append("{");
         tabCount++;
      }
      return null;
   }

   ///Outputs ExpressionBlock code for this RefBlock
   ///Will only do tabbing and closing if its a top level call
   private BVReturn expressionRef(RefBlock block, boolean top)
   {
      if(top) code.append("\n");
      ExpressionBlock tempBlock = (ExpressionBlock)block.getBlock();
      if(top) addTabs(tabCount);
      ///Handle the first input
      if(block.getParams().get(0).getBlock().getType() == BlockTypes.VAR)
      {
         writeParam(block.getParams().get(0));
         //code.append(Block.toCallName(tempBlock.getFirstIn().getName()));
      }
      else if(block.getParams().get(0).getBlock().getType() == BlockTypes.EXPRESSION)
      {
         expressionRef(block.getParams().get(0),false);
      }
      else
      {
         System.err.println("Error:Expression can only contain vars and expressions");
      }
      
      ///add the symbol
      code.append(" " + tempBlock.getSymbol() + " ");
      
      ///deal with the second input
      if(block.getParams().get(1).getBlock().getType() == BlockTypes.VAR)
      {
         writeParam(block.getParams().get(1));
         //code.append(Block.toCallName(tempBlock.getSecondIn().getName()));
      }
      else if(block.getParams().get(1).getBlock().getType() == BlockTypes.EXPRESSION)
      {
         expressionRef(block.getParams().get(1),false);
      }
      else
      {
         System.err.println("Error:Expression can only contain vars and expressions.");
      }
      
      ///And end the expression if this is the top level call
      if(top) code.append(";");
      return null;
   }
   

   ///Refs to VarBlock types should not be called.
   private BVReturn varRef(RefBlock block)
   {
      ///This should not happen since var refs only get used in things not called
      ///directly.
      System.err.println("Error:Unassociated Var RefBlock.");
      return null;
   }

   ///Outputs code for the object method call this references.
   ///RefBlocks should only point to ObjectBlock types if they're calling a
   ///method within the object, in which case the first parameter should be
   ///a reference to the method being called.
   private BVReturn objectRef(RefBlock block)
   {
      ObjectBlock tempBlock = (ObjectBlock)block.getBlock();
      if(block.getParams().size() > 0)
      {
         code.append("\n");
         RefBlock methodRef = block.getParams().get(0);
         addTabs(tabCount);
         
         ///Check if we're returning something
         if(methodRef.getReturns().size() > 0)
         {
            code.append(Block.toCallName(methodRef.getReturns().get(0).getName()));
            code.append(" = ");
         }
         ///Add the name of the object
         code.append(tempBlock.getCallName());
         ///Check if we're creating a constructor
         if(methodRef.getBlock().getType() == BlockTypes.CODE)
         {
            if(((CodeBlock)methodRef.getBlock()).isConstructor())
            {
               code.append(" = new ");
            }
            else code.append(".");
            ///Do the method call
            codeRef(block.getParams().get(0), false);
         }
         else if(methodRef.getBlock().getType() == BlockTypes.BLOCK)
         {
            if(((BlockBlock)methodRef.getBlock()).isConstructor())
            {
               code.append(" = ");
            }
            else code.append(".");
            ///Do the method call
            blockRef(block.getParams().get(0), false);
         }
         else
            System.err.println("Error:Can only reference methods in an object.");
      }
      else
         System.err.println("First param in object ref must contain method ref");
      ///And finish this call
      code.append(";");
      
      return null;
   }

   ///CommentBlock refs just visit the block itself
   private BVReturn commentRef(RefBlock block)
   {
      ///For comments we just need to visit the block itself
      block.getBlock().accept(this);
      return null;
   }
   
   ///Returns the type of visitor this is
   
   public BVTypes getBVType()
   {
      
      return BVTypes.JAVA;
   }

   ///ObjectBlock types should never be visited directly by this, just
   ///refs to them.
   
   public BVReturn visit(ObjectBlock block)
   {
      ///There's no reason for the JavaWriter to visit an ObjectBlock.
      ///It should only be visiting VarBlocks that use ObjectBlocks or
      ///RefBlocks that reference ObjectBlocks.
      return null;
   }

   ///When we visit a CommentBlock just output the comment at the correct
   ///tab height
   
   public BVReturn visit(CommentBlock block)
   {
      code.append("\n");
      addTabs(tabCount);
      ///Write the comment into the buffer.
      code.append("/**\n");
      addTabs(tabCount);
      ///We don't block closing comments here in case someone wants to
      ///add a specific line of code as a hack. Will probably make this
      ///a 'feature' that can be turned on or off as part of the editor.
      code.append(block.getComment());
      code.append("\n");
      addTabs(tabCount);
      code.append("*/");
      return null;
   }

   
   public BVReturn visit(JJClass jjclass)
   {
      String tempStr;
      tabCount = 0;
      if(jjclass == null) return null;
      this.clear();
      filename = new String(jjclass.getCallName() + ".java");
      className = new String(jjclass.getCallName());
      
      ///Write all the imports
      Iterator<String> sit = jjclass.getImports().iterator();
      while(sit.hasNext())
      {
         code.append("import " + sit.next() + ";\n");
      }
      
      ///Write a comment opening string
      code.append("\n/**\n");
      tempStr = jjclass.getComment();
      ///make sure there aren't any closing comment marks in the comment.
      ///Extra code here would be bad.
      if(tempStr != null) tempStr.replaceAll("\\*/", "x/");
      ///Add it to the code
      code.append(tempStr);
      ///Close the comment
      code.append("\n*/");
      
      ///Write the name of the class and open brace
      code.append("\npublic class ");
      code.append(jjclass.getCallName());
      code.append("\n{");
      tabCount = 1;
      
      ///Iterate through all the class level variables
      Iterator<VarBlock> vIt = jjclass.getVars().iterator();
      ///and visit them
      while(vIt.hasNext())
      {
         vIt.next().accept(this);
      }
      ///Next iterate through the constructors
      Iterator<BlockInterface> bIt = jjclass.getConstructors().iterator();
      while(bIt.hasNext())
      {
         bIt.next().accept(this);
      }
      ///Finally, iterate through all the methods
      bIt = jjclass.getMethodBlocks().iterator();
      while(bIt.hasNext())
      {
         bIt.next().accept(this);
      }
      if(tabCount != 1)
         System.err.println("Error:Indent misalignment");
      ///close the class
      tabCount = 0;
      code.append("\n}");
      
      return null;
   }

   ///Writes the generated code for any write flags that are set
   
   public BVReturn finish()
   {
      if(className == null || className.length() < 1)
      {
         System.err.println("Error:Cannot write class without a name.");
         return null;
      }
      if(writeToFile)
      {
         try
         {
            BufferedWriter out = new BufferedWriter(new FileWriter(filename));
            out.write(code.toString());
            out.close();
         } catch(IOException e)
         {
            System.err.println(e.getMessage());
         }
         
      }
      if(writeToBlueJ)
      {
         //TODO write code to output to BlueJ
      }
      return null;
   }
   
   ///Test method for the writer
   public static void main(String[] args)
   {
      JJClass helloWorld;
      JJProject helloWorldProj;
      
      BVJavaWriter jWrite = new BVJavaWriter();
      
      try
      {
         helloWorldProj = JJProject.getTestProject();
         helloWorld = helloWorldProj.getClass("Hello World");
         helloWorld.accept(jWrite);
         
         jWrite.setWrite(true, false);
         jWrite.finish();
         
         helloWorld.setName("hWorld");
         helloWorld.accept(jWrite);
         jWrite.setWrite(true, false);
         jWrite.finish();
         
      } catch(Exception e)
      {
         System.err.println(e.getMessage());
      }
      
   }

   
   public BVReturn visit(JJLibrary jjlib)
   {
      //Java Writer never visits a library
      return null;
   }

   
   public BVReturn visit(JJProject jjproj)
   {
      //Java Writer never visits a project either
      Iterator<JJClass> cIt = jjproj.getClasses().iterator();
      while(cIt.hasNext())
      {
         cIt.next().accept(this);
         this.setWrite(true, false);
         this.finish();
      }
      return null;
   }

}
